﻿# -*- coding: utf-8 -*-
'''
Created on 16.06.2011

@author: Andrey Gaidel
'''

import models
import sql
from datetime import datetime
from decimal import Decimal
from django import http

EXCELLENT = u"Отличник"
GOOD_EXCELLENT = "Учится на «Хорошо» и «Отлично»"
GOOD = u"Хорошист"
MEDIOCRE = u"Троечник"
WEAK = u"Двоечник"

CONTRACT = u"Контрактник"

ORPHAN = u"Сирота"
INVALID = u"Инвалид"
VETERAN = u"Ветеран"

RADIATION = u"Пострадавший от радиации"

SCIENTIST = u"Научный работник"
SPORTSMAN = u"Спортсмен"

TRANSPORT = u"Ездит на транспорте"
MANY_CHILDREN = u"Многодетная семья"
DORMITORY = u"Проживает в общежитии"

CLASSES = (EXCELLENT, GOOD_EXCELLENT, GOOD, MEDIOCRE, WEAK, CONTRACT, ORPHAN,
        INVALID, VETERAN, RADIATION, SCIENTIST, SPORTSMAN, TRANSPORT,
        MANY_CHILDREN, DORMITORY)
RESTRICTIONS = set([WEAK, CONTRACT])
ACADEMIC = (EXCELLENT, GOOD_EXCELLENT, GOOD, MEDIOCRE, WEAK)
COEFFICIENTS = {
    EXCELLENT: 2.5,
    GOOD_EXCELLENT: 1.5,
    GOOD: 1.0,
    RADIATION: 1.0,
    SCIENTIST: 0.5,
    SPORTSMAN: 0.5,
}
SPECIALS = {
    EXCELLENT: 2.5,
    GOOD_EXCELLENT: 1.5,
    GOOD: 1.0,
    MEDIOCRE: 1.0,
    WEAK: 0.0,
}
PAYMENTS = {
    TRANSPORT: 1000,
    ORPHAN: 1000,
    MANY_CHILDREN: 1000,
    DORMITORY: 1000,
}

class CommitteeException(Exception):
    
    def __init__(self, message):
        self.message = message
    
    def __str__(self):
        return self.message.encode('utf-8')
    
    def __unicode(self):
        return unicode(self.message)

def get_class(marks):
    clazz = None
    if 2 in marks:
        clazz = WEAK
    elif 3 in marks:
        clazz = MEDIOCRE
    elif 4 in marks and 5 not in marks:
        clazz = GOOD
    elif 4 in marks and 5 in marks:
        clazz = GOOD_EXCELLENT
    elif 5 in marks:
        clazz = EXCELLENT
    if clazz is None:
        return None
    try:
        return models.StudentClass.objects.get(name=clazz)
    except models.StudentClass.DoesNotExist:
        return None

def fill_database():
    for name in CLASSES:
        try:
            models.StudentClass.objects.get(name=name)
        except models.StudentClass.DoesNotExist:
            models.StudentClass(name=name,
                    coefficient=str(COEFFICIENTS.get(name, 0.0)),
                    payment=PAYMENTS.get(name, 0),
                    restriction=(name in RESTRICTIONS)).save()

def fill_academic_progress():
    fill_database()
    for student in list(models.Student.objects.all()):
        marks = set()
        for exam in models.get_by_raw_SQL(models.Examination, sql.LAST_EXAMINATIONS,
                (student.pk, student.group.pk)):
            temp = list(models.Mark.objects.filter(student=student, examination=exam))
            if len(temp) == 0:
                mark = 2
            else:
                mark = max([m.grade for m in temp])
            marks.add(mark)
        clazz = get_class(marks)
        if clazz is not None:
            for c in ACADEMIC:
                try:
                    student.stud_classes.remove(models.StudentClass.objects.get(name=c))
                except models.StudentClass.DoesNotExist:
                    pass
            student.stud_classes.add(clazz)
            student.save()

def calc_scholarship(student):
    fill_academic_progress()
    last_scholarships = models.get_by_raw_SQL(models.Scholarship,
            sql.LAST_SCHOLARSHIP, (student.pk, student.pk))
    if len(last_scholarships) > 0:
        raise CommitteeException(u"Стипендия уже выдана этому студенту")
    classes = {}
    for clazz in student.stud_classes.all():
        if clazz.restriction:
            raise CommitteeException(u"Этот студент относится к категории %s, которая не допускает выдачу стипендии" % unicode(clazz))
        classes[clazz.name] = clazz
    ans = {}
    min_wage = models.get_by_raw_SQL(models.MinWage, sql.LAST_MINWAGE, ())
    if len(min_wage) != 1:
        raise CommitteeException(u"Не удаётся получить актуальный минимальный размер стипендии")
    else:
        min_wage = min_wage[0].money
    if EXCELLENT in classes:
        ans[classes[EXCELLENT]] = classes[EXCELLENT].coefficient * min_wage
    elif ORPHAN in classes or INVALID in classes or VETERAN in classes:
        clazz = None
        for c in ACADEMIC:
            if c in classes:
                clazz = c
                break
        if clazz is not None:
            ans[classes[clazz]] = Decimal(repr(SPECIALS[clazz])) * min_wage
    if RADIATION in classes:
        ans[classes[RADIATION]] = classes[RADIATION].coefficient * min_wage
    return ans

def calc_scholarship_payment(clazz):
    min_wage = models.get_by_raw_SQL(models.MinWage, sql.LAST_MINWAGE, ())
    if len(min_wage) != 1:
        raise CommitteeException(u"Не удаётся получить актуальный минимальный размер стипендии")
    else:
        min_wage = min_wage[0].money
    return clazz.coefficient * min_wage

def total_payment(payments):
    ans = Decimal('0.0')
    for payment in payments:
        if type(payments[payment]) == Decimal:
            ans = ans + payments[payment]
    return ans

def give_scholarship(plan):
    total = sum(plan.values())
    fond = models.get_by_raw_SQL(models.SSF, sql.LAST_SSF, ())
    if len(fond) == 0:
        fond = Decimal('0.0')
    else:
        fond = fond[0].money
    date = datetime.now()
    ssf = models.SSF(money=(fond - total), date=date)
    ssf_diff = models.SSFDiff(balance=-total, date=date)
    ssf.save()
    ssf_diff.save()
    for student in plan:
        session = models.get_by_raw_SQL(models.Session, sql.LAST_SESSION, (student.pk,))
        if len(session) == 0:
            continue
        else:
            session = session[0]
        cash = models.Cash(payment=plan[student] / monthes_till_next_session(student), ssf_diff=ssf_diff)
        cash.save()
        scholarship = models.Scholarship(session=session, student=student, cash=cash)
        scholarship.save()

def give_welfare(plan, requests, date):
    total = sum(plan.values())
    fond = models.get_by_raw_SQL(models.SSF, sql.LAST_SSF, ())
    if len(fond) == 0:
        fond = Decimal('0.0')
    else:
        fond = fond[0].money
    now = datetime.now()
    ssf = models.SSF(money=(fond - total), date=now)
    ssf_diff = models.SSFDiff(balance=-total, date=now)
    ssf.save()
    ssf_diff.save()
    for student in plan:
        cash = models.Cash(payment=plan[student], ssf_diff=ssf_diff)
        cash.save()
        welfare = models.Welfare(student=student, cash=cash, date=date)
        if student in requests:
            welfare.welfare_request = requests[student]
        welfare.save()

def monthes_till_next_session(student):
    session = models.get_by_raw_SQL(models.Session, sql.LAST_SESSION, (student.pk,))
    if len(session) == 0:
        return 0
    session = session[0]
    next_session = models.get_by_raw_SQL(models.Session, sql.NEXT_SESSION, (session.pk,))
    if len(next_session) == 0:
        if session.number & 1 == 0:
            return 7
        else:
            return 5
    next_session = next_session[0]
    current = session.end_date
    ans = 0
    while current <= next_session.end_date:
        current = current.replace(year=current.year + current.month / 12, month=current.month % 12 + 1, day = 1)
        ans = ans + 1
    return ans - 1

def check_request(request):
    return models.Welfare.objects.filter(welfare_request=request).count() == 0

def calc_welfare(student):
    fill_academic_progress()
    ans = {}
    for clazz in student.stud_classes.all():
        if clazz.restriction:
            raise CommitteeException(u"Этот студент относится к категории %s, которая не допускает выдачу социальных пособий" % unicode(clazz))
        if clazz.payment > Decimal('0.0'):
            ans[clazz] = clazz.payment
    request = models.get_by_raw_SQL(models.WelfareRequest, sql.LAST_REQUEST, (student.pk,))
    if len(request) != 0:
        request = request[0]
        if check_request(request):
            ans[request] = request.cash
    return ans
